/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2018年6月25日
 * V4.0
 */
package com.jphenix.driver.guard;

import com.jphenix.share.lang.SBoolean;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.SFilesUtil;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.viewhandler.IViewHandler;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 文件分发配置信息容器
 * 
 * 配置信息：
 * <file_guard disabled="0">
 *   <group_1 disabled="0 mode="share/master" master="server1">
 *     <path value="">
 *       <exclude></exclude>
 *     </path>
 *     <spec type="reboot"></spec>
 *   </group_1>
 * </file_guard>
 * 
 * 注意：路径可以是绝对路径，也可以是相对路径，相对于当前项目的WEB-INF文件夹
 *       配置中如果为：<path> 没有value值，默认为当前项目根路径，即：/WEB-INF/..
 * 
 * 排除信息规则：
 * 注意：符号*可以出在开头，中间和末尾
 * 
 *  星号开头，代表末尾匹配
 *  星号末尾，代表开头匹配
 *  如果不带星号，代表完全匹配  //不能默认开头匹配，因为比如：不同步/WEB-INF/lib/jphenix_sdk.jar 但是需要同步 /WEB-INF/lib/jphenix_sdk.jar.new
 *  如果星号在中间，星号的左侧与路径开头匹配，星号右侧与路径末尾匹配
 * 
 * 
 * /path/*          指定路径下的文件
 * 
 * 2018-06-29 增加了文件所属群组信息，支持一台服务器为不同群组提供文件分享服务
 * 2018-07-27 增加了统计信息变量
 * 2020-10-04 配置信息中增加了默认为当前项目的设置，即：<path>，不带value属性值
 * 2020-10-05 完善了注释
 * 
 * @author MBG
 * 2018年6月25日
 */
@ClassInfo({"2020-10-05 21:47","文件分发配置信息容器"})
public class GuardConfigVO {

  private FileGuardFilter ff = null; //文件分发过滤器

	/**
	 * 是否禁用文件分发服务
	 */
	public boolean disasbled = true;
	
	/**
	 * 路径信息序列
	 */
	public List<String> pathList = null;
	
	/**
	 * 目标群组名序列（与路径信息序列对应）
	 */
	public List<String> groupList = null;
	
	/**
	 * 路径对应的排除信息序列 
	 * key:路径  value:List<String> 排除信息序列
	 */
	public Map<String,List<String>> excludeMap = null;
	
	/**
	 * 特殊处理信息序列 
	 * key  : 特殊处理关键字，比如reboot
	 * value: 需要特殊处理的信息关键字，比如路径，文件名关键字
	 */
	public Map<String,List<String>> specMap = null;
	
	/**
	 * 是否为共享模式
	 */
	public boolean isShareMode = true;
	
	/**
	 * 如果不是共享模式，本机是否为主服务器
	 */
	public boolean isMasterServer = false;
	
	/**
	 * 如果不是共享模式，主服务器的名字值
	 */
	public String masterServerName = null;
	
	/**
	 * 是否已经执行了初始化
	 */
	public boolean hasInited = false;
	
	
	/**
	 * 构造函数
	 * @author MBG
	 */
	public GuardConfigVO(FileGuardFilter ff) {
    super();
    this.ff = ff;
	}

	/**
	 * 初始化配置信息
	 * @param vh              配置信息对象
	 * @param localServerName 当前服务器在集群中的名字
	 * @param localGroupName  当前服务器所在集群的分组名
	 * 2018年6月25日
	 * @author MBG
	 */
	public void init(IViewHandler vh,String localServerName,String localGroupName) {
		
		hasInited = true;
		
		pathList   = new ArrayList<String>();
		groupList  = new ArrayList<String>();
		
		excludeMap = new HashMap<String,List<String>>();
		specMap    = new HashMap<String,List<String>>();
		
		disasbled  = SBoolean.valueOf(vh.a("disabled"));
		//获取当前分组的文件分配信息
		vh = vh.fnn(localGroupName);
		if(!disasbled) {
			disasbled = SBoolean.valueOf(vh.a("disabled"));
		}
		
		isMasterServer = false;
		if("share".equals(vh.a("mode").toLowerCase())) {
			isShareMode      = true;
			masterServerName = "";
		}else {
			isShareMode      = false;
			masterServerName = vh.a("master");
			if(masterServerName.equals(localServerName)) {
				isMasterServer = true;
			}
		}
		List<IViewHandler> pathVhList      = vh.cnn("path");  //获取作业路径信息序列
		List<IViewHandler> excludeVhList;                     //排除信息对象序列
		List<String>       excludeList;                       //排除信息序列
		String             value;                             //信息值
		String             cValue;                            //子信息值
		for(IViewHandler pathVh:pathVhList) {
			value = pathVh.a("value");
			if(value.length()<1) {
        //如果值为空，默认为当前项目
        value = ff.path();
			}
			value = BaseUtil.swapString(value,"\\","/");
			pathList.add(value);
			groupList.add(pathVh.a("group")); //放入集群分组名信息（可为空，默认当前集群分组）
			
			if(pathVh.hasChildNodeByNodeName("exclude")) {
				excludeVhList = pathVh.cnn("exclude");
				excludeList   = excludeMap.get(value);
				if(excludeList==null) {
					excludeList = new ArrayList<String>();
					excludeMap.put(value,excludeList);
				}
				for(IViewHandler eVh:excludeVhList) {
					cValue = eVh.nt().trim();
					if(cValue.length()<1) {
						continue;
          }
          if(!cValue.startsWith("*")){
            if(!cValue.startsWith("/")){
              cValue = "/"+cValue;
            }
          }
          cValue = BaseUtil.swap(cValue,"\\","/");
					excludeList.add(cValue);
				}
			}
		}
		//解析特殊处理信息
		if(vh.hasChildNodeByNodeName("spec")) {
			//获取特殊处理信息
			List<IViewHandler> specVhList = vh.cnn("spec");
			List<String>       specList; //特殊处理信息
			String             specType; //特殊处理关键字
			for(IViewHandler cVh:specVhList) {
				specType = cVh.a("type");
				value    = cVh.nt();
				if(specType.length()<1 || value.length()<1) {
					continue;
				}
				specList = specMap.get(specType);
				if(specList==null) {
					specList = new ArrayList<String>();
					specMap.put(specType,specList);
				}
				specList.add(value);
			}
		}
	}
	
	/**
	 * 检测文件是否按规则匹配
	 * 注意：符号*只能出现规则字符串的末尾
	 * 注意： 路径信息都不区分大小写
	 * 
     * /path/*          指定路径下的文件
     * /path/           同上
     * /path            同上
     * 
     * /path/sub_name*  指定路径下，匹配文件名关键字的
     * /path/sub_name   同上
     * 
     * file_sub_name*   匹配文件名关键字
     * file_name        文件名完全符合
	 * 
	 * @param path   需要验证的路径
	 * @param rule   规则信息
	 * @return       true匹配规则
	 * 2018年6月26日
	 * @author MBG
	 */
	private boolean check(String path,String rule) {
		if(rule==null || rule.length()<1 || path==null || path.length()<1) {
			return false;
		}
		//分隔位置
		int point = rule.indexOf("/*");
		if(point>-1) {
			//去掉末尾的符号
			rule = rule.substring(0,point+1);
		}
		point = rule.indexOf("*");
		//规则中,路径分隔符位置
		int point2 = rule.indexOf("/");
		//路径中，路径分隔符的位置
		int pathPoint = path.lastIndexOf("/");
		if(point2>-1) {
			//存在文件路径
			if(point>-1) {
				rule = rule.substring(0,point);
			}
			return path.toLowerCase().indexOf(rule.toLowerCase())>-1;
		}else if(point>-1) {
			//纯文件名，包含通配符
			if(pathPoint>0) {
				path = path.substring(pathPoint+1);
			}
			rule = rule.substring(0,point);
			return path.toLowerCase().indexOf(rule.toLowerCase())>-1;
		}else {
			//纯文件名（文件名完全匹配，不区分大小写，别较真，在Linux中，也不要人为的弄出不同大小写相同名字的多个文件）
			return path.toLowerCase().endsWith(rule.toLowerCase());
		}
	}
	
	
	/**
	 * 判断处理指定文件后，是否需要重启服务
	 * @param file 指定文件对象
	 * @return
	 * 2018年6月26日
	 * @author MBG
	 */
	public boolean needReboot(File file) {
		if(file==null) {
			return false;
		}
		//获取需要重启的规则信息序列
		List<String> rebootRuleList = specMap.get("reboot");
		if(rebootRuleList!=null) {
			for(String rule:rebootRuleList) {
				if(check(file.getPath(),rule)) {
					return true;
				}
			}
		}
		return false;
	}
	
	
	/**
	 * 是否为有效的，并且需要进行管理的文件路径
	 * @param basePath 根路径
	 * @param path     指定的绝对路径
	 * @return 对应的文件对象
	 * 2018年6月26日
	 * @author MBG
	 */
	public File validFile(String basePath,String path) {
		//排除信息序列
		List<String> excludeList = excludeMap.get(basePath);
		if(excludeList!=null) {
			for(String excludeEle:excludeList) {
				if(check(path,excludeEle)) {
					return null;
				}
			}
		}
		//构建返回值
		File reFile = new File(SFilesUtil.getAllFilePath(path,basePath));
		if(!reFile.exists()) {
			return null;
		}
		return reFile;
	}
}
